package com;

import org.apache.log4j.Logger;

import com.model.Dealer;
import com.model.Hand;
import com.model.Human;
import com.model.Shoe;
import com.strategies.DealerStrategy;
import com.strategies.IStrategy;
import com.strategies.PlayerStrategy1;

/**
 * 
 * @author andrew_carzoli
 *
 *Things to Add:
 * - multiple splitting
 * - cannot double down on Splitted Aces(implemented) and cannot hit on splitted Aces(not implemented)
 *
 */
public class BlackJackGame {
	private static final int PERCENT_TO_SHUFFLE_SHOE = 15;
	private static final double MINIMUM_BET = 6;
	private static final double NUMBER_OF_HOURS = 100000; //100000 = 0.733% edge
	private static final double NUMBER_OF_HANDS_PER_HOUR = 200; // Average per person(s) - 210, 140, 105, 84, 70, 60, 53 
	private static double numberOfHands = NUMBER_OF_HANDS_PER_HOUR * NUMBER_OF_HOURS;  
	private static Shoe shoe;
	private final static int NUMBER_OF_DECKS = 6;
	//private static double playersBet = 0;
	private static final Logger logger = Logger.getLogger(BlackJackGame.class.getSimpleName());
	
	public void play() {
		if(logger.isInfoEnabled()) logger.info("************************* Starting BlackJack Game ************************");
		
		shoe = new Shoe(NUMBER_OF_DECKS);
		int originalShoeSize = shoe.size();
		
		Human player = new Human(MINIMUM_BET);
		Dealer dealer = new Dealer();

		IStrategy playerStrategy = new PlayerStrategy1();
		IStrategy dealerStrategy = new DealerStrategy();
		
		while (numberOfHands > 0) {
			player.clearHands();
			dealer.clearHands();
			
			Hand playerHand = new Hand(playerStrategy, MINIMUM_BET);
			Hand dealerHand = new Hand(dealerStrategy);
			
			playerHand.addCard(shoe.getCard());
			dealerHand.addCard(shoe.getCard());
			playerHand.addCard(shoe.getCard());
			dealerHand.addCard(shoe.getCard());
			
			player.addHand(playerHand);
			dealer.addHand(dealerHand);
			
			Hand dealersHand = dealer.getHand();

			
			Hand playersHand = player.getHand();
			if (dealersHand.hasBlackJack()) {
				if (playersHand.hasBlackJack()) {
					if(logger.isDebugEnabled()) logger.debug("Player and Dealer has BlackJack\n");
					if(logger.isDebugEnabled()) logger.debug("Push");
					continue;
				}
				else {
					if(logger.isDebugEnabled()) logger.debug("Dealer has BlackJack");
					if(logger.isDebugEnabled()) logger.debug("Dealer Won");
					player.removeFromBalance(playersHand.getBet());
					continue;
				}
			}
			else if (playersHand.hasBlackJack()) {
				if(logger.isDebugEnabled()) logger.debug("Player has BlackJack");
				if(logger.isDebugEnabled()) logger.debug("Player Won");
				player.addToBalance((playersHand.getBet() * 1.5));
				continue;
			}
			else { 
				//FIXME: We are only splitting once. 
				if (playersHand.shouldSplit(dealersHand.getFirstCard())) { //Enter if Player is splitting the hand
					Hand tempHand = playerHand;
					player.clearHands();
					Hand newHand1 = new Hand(playerStrategy, MINIMUM_BET, false);
					Hand newHand2 = new Hand(playerStrategy, MINIMUM_BET, false);
					
					newHand1.addCard(tempHand.getFirstCard());
					newHand2.addCard(tempHand.getSecondCard());
					newHand1.addCard(shoe.getCard());
					newHand2.addCard(shoe.getCard());
					
					player.addHand(newHand1);
					player.addHand(newHand2);
				}
				for (Hand plHand1 : player.getHands()) {
					if (plHand1.shouldDouble(dealersHand.getFirstCard())) { //Enter if Player is doubling down
						playersHand.setBet(playersHand.getBet() * 2);
						plHand1.addCard(shoe.getCard());
						if(logger.isDebugEnabled()) logger.debug("Player Double Down");
					}
					else {
						while (plHand1.shouldHit(dealersHand.getFirstCard())) {
							plHand1.addCard(shoe.getCard());
						}
					}
				}
				
				while (dealersHand.shouldHit(null)) {
					dealersHand.addCard(shoe.getCard());
				}

				determineWinner(player, dealer);
			}
				
			
			if ((((float)shoe.size() / (float)originalShoeSize) * 100) < PERCENT_TO_SHUFFLE_SHOE) {
				shoe.newShoe();
				//System.out.println("1 " + shoe.toString());
			}
			
			numberOfHands--;
			if(logger.isDebugEnabled()) logger.debug("");
		}
		
		double totalHands = NUMBER_OF_HOURS * NUMBER_OF_HANDS_PER_HOUR;
		float averageLossPerHand = ((float)player.getBalance() / (float)totalHands);
		double totalAmountBet = player.getBet() * totalHands;
		
		if(logger.isInfoEnabled()) logger.info("");
		if(logger.isInfoEnabled()) logger.info("Player Bet: " + player.getBet() + "$");
		if(logger.isInfoEnabled()) logger.info("Player Balance: " + player.getBalance() + "$");
		if(logger.isInfoEnabled()) logger.info("Average Profit Per Hour: " + (int)(player.getBalance() / NUMBER_OF_HOURS) + "$");
		if(logger.isInfoEnabled()) {logger.info("Average Profit Per Hand: " + averageLossPerHand + "$");}
		if(logger.isInfoEnabled()) logger.info("House edge: " + (averageLossPerHand/player.getBet()) * 100 + "%");
		if(logger.isInfoEnabled()) logger.info("Correct House edge: " + (player.getBalance()/totalAmountBet) * 100 + "%");
		
		if(logger.isInfoEnabled()) logger.info("********************** End BlackJack Game ********************************");
	}

	private void determineWinner(Human player, Dealer dealer) {
		printTableHands(player, dealer);
		Hand dealerHand = dealer.getHand();
		int dealerHandValue = dealerHand.getTotalHandValue();
		
		for (Hand hand : player.getHands()) {
			int playerHandValue = hand.getTotalHandValue();
			if (hand.hasBusted() || (!dealerHand.hasBusted() && dealerHandValue > playerHandValue)) {
				if(logger.isDebugEnabled()) logger.debug("Dealer Won");
				player.removeFromBalance(hand.getBet());
			}
			else if (dealerHand.hasBusted() || (playerHandValue > dealerHandValue)) {
				if(logger.isDebugEnabled()) logger.debug("Player Won");
				player.addToBalance(hand.getBet());
			} else {
				if(logger.isDebugEnabled()) logger.debug("Push");
			}
		}
		if(logger.isDebugEnabled()) logger.debug("Players balance: " + player.getBalance());
	}

	private static void printTableHands(Human player, Dealer dealer) {
		for (Hand hand : player.getHands()) {
			if(logger.isDebugEnabled()) logger.debug("Player Hand = " + hand.toString() + "  Total = [" + hand.getTotalHandValue() + "]");
		}
		if(logger.isDebugEnabled()) logger.debug("Dealer Hand = " + dealer.getHand().toString() + "  Total = [" + dealer.getHand().getTotalHandValue() + "]");
		//if(logger.isDebugEnabled()) logger.debug("Player Total = [" + player.getTotalHandValue() + "]");
		//if(logger.isDebugEnabled()) logger.debug("Dealer Total = [" + dealer.getTotalHandValue() + "]");
	}

}


//FIXME: Improve Humand and Dealer classes to inheret methods
